import { KeyboardAPI, shiftFrom16Bit, shiftTo16Bit } from "./KeyboardAPI.js";
import { WebHid } from "./WebHid.js";

// 连接设备的HID接口
export const connectDeviceHID = async (filters = []) => {
  const devices = await WebHid.devices(false, filters);
  
  if (devices.length > 1) {
    console.log("检测到多个设备:", devices);
    const sortDevices = devices.sort((a, b) => a.vendorId - b.vendorId);
    
    // 当设备数量大于1时，通过获取设备信息判断设备类型
    for (const device of sortDevices) {
      try {
        console.log("正在检查设备:", device);
        const tempComm = new DeviceComm(new KeyboardAPI(device.address));
        
        // 获取设备信息来判断设备类型
        const deviceInfo = await tempComm.getDeviceInfoWithRetry(0, 1, 100);
        console.log("设备信息:", deviceInfo);
        
        // 检查设备工作模式，如果是AA（有线模式）则连接，如果是BB（2.4G模式）则跳过
        if (deviceInfo.curMode === 0x01) {
          console.log("检测到有线设备，正在连接...");
          return tempComm;
        } else if (deviceInfo.curMode === 0x02) {
          console.log("检测到2.4G设备，跳过连接");
          // 清理临时连接
          tempComm.cleanup();
          continue;
        } else if (deviceInfo.curMode === 0x03) {
          console.log("检测到有线设备，正在连接...");
          return tempComm;
        }
        else {
          console.log("未知设备模式:", deviceInfo.curMode.toString(16));
          // 清理临时连接
          tempComm.cleanup();
          continue;
        }
      } catch (error) {
        console.error("获取设备信息失败:", error);
        // 如果获取设备信息失败，继续检查下一个设备
        continue;
      }
    }
    
    console.log("未找到合适的有线设备");
    return undefined;
  } else if (devices.length == 1) {
    console.log("检测到单个设备，直接连接");
    return new DeviceComm(new KeyboardAPI(devices[0].address));
  } else {
    console.log("未检测到任何设备");
    return undefined;
  }   
};

export const shiftLoFrom16Bit = (value) => {
  return value & 0xFF; // 使用 0xFF 而不是 255，虽然在这里两者等价，但 0xFF 更清晰地表示是8位掩码
};

export const shiftHIFrom16Bit = (value) => {
  return value >> 8; // 右移8位以获取高位字节
};

// 设备通讯接口
export class DeviceComm {
  constructor(api) {
    this.demoMode = false;
    this.api = api;
    this.vendorId = 0;
    this.productId = 0;
    this.productName = "Test Keyboard";
    this.onListeners = [];
    this.mode = 'usb';
    this.onChangeDpi = undefined;
    
    // 添加写入状态标记
    this.isWriting = false;
    this.writeTimeout = null;

    const hid = this.api.getHID();
    this.vendorId = hid.vendorId;
    this.productId = hid.productId;
    this.productName = hid.productName;
    this.addListeners();
  }

  addListeners() {
    const fn = (evt) => {
      let notifyValue = {};
      const data = new Uint8Array(evt.data.buffer);

      if (data[2] == 0x81 && data[3] == 0x0F) {
        // 如果是写入后的上报数据，忽略
        if (this.isWriting) {
          console.log("忽略写入后的上报数据:", data);
          return;
        }
        // 设备主动上报的状态数据
        notifyValue = data;
        console.log("设备主动上报状态:", notifyValue);
        const listener = this.onListeners.find(
          (listener) => listener.name == 'statu'
        );
        listener && listener.fb(notifyValue);
      }
    };
    this.api.getHID().addListeners(fn);
  }

  // 获取报告ID
  getReportId() {
    return this.api.getHID().reportId;
  }

  // 获取连接模式
  async getConnectMode() {
    return this.api.getConnectMode();
  }

  // 设置数据
  async setData(data) {
    // 设置写入状态标记
    this.isWriting = true;
    
    // 清除之前的超时
    if (this.writeTimeout) {
      clearTimeout(this.writeTimeout);
    }
    
    // 设置新的超时，100ms后清除写入状态
    this.writeTimeout = setTimeout(() => {
      this.isWriting = false;
    }, 100);

    return this.api.sendDeviceData(data);
  }

  // 创建延迟函数
  async delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // 获取设备信息，带重试机制
  async getDeviceInfoWithRetry(reportId = 0, maxRetries = 10, retryDelay = 50) {
    let retryCount = 0;
    let lastError = null;

    while (retryCount < maxRetries) {
      try {
        // 尝试获取设备信息
        const deviceInfo = await this.getDeviceInfo(reportId);
        console.log(`设备信息获取成功，尝试次数: ${retryCount + 1}`);
        return deviceInfo;
      } catch (error) {
        lastError = error;
        retryCount++;
        
        if (retryCount < maxRetries) {
          console.warn(`设备信息获取失败，正在进行第 ${retryCount + 1} 次重试...`);
          // 等待指定的延迟时间后再重试
          await this.delay(retryDelay);
        }
      }
    }

    // 如果所有重试都失败，抛出最后捕获的错误
    console.error(`设备信息获取失败，已重试 ${maxRetries} 次`);
    throw lastError || new Error("获取设备信息失败");
  }

  /**
   * 获取设备信息
   * @param {number} reportId - 报告ID，默认为0
   * @returns {Promise<Object>} 设备信息对象
   * @throws {Error} 当设备返回错误或数据格式无效时抛出错误
   */
  async getDeviceInfo(reportId = 0) {
    // 1. 准备请求数据
    const buffer = new Array(65).fill(0);
    buffer[0] = reportId;
    buffer[1] = 0x55;  // 固定标识
    buffer[2] = 0xAA;  // 固定标识
    buffer[3] = 0x80;  // 获取设备信息命令

    // 2. 发送请求并获取响应
    const data = await this.setData(buffer);
    console.log("设备信息响应数据:", data);

    // 3. 验证响应数据
    let status = this.isValidDeviceInfoResponse(data);
    if (status == 0) {
      console.error("设备信息响应无效:", data);
      throw new Error("获取设备信息失败: 设备返回无效数据");
    } else if (status == 2) {
      console.error("设备在线, 但数据异常", data);
      throw new Error("设备在线, 但数据异常");
    }

    // 4. 解析设备信息
    // 数据格式：
    // [0-3]: 命令头
    // [4]: battStatus (1字节) - 充电BIT7/充满BIT6/低电BIT5
    // [5]: battPercent (1字节) - 电量值
    // [6]: curMode (1字节) - 设备当前工作模式
    // [7]: reserved (1字节)
    // [8-23]: versionStr (16字节) - 版本号
    // [24-25]: drvSN (2字节) - 驱动编号
    // [26-27]: devicePID (2字节)
    // [28-29]: deviceVID (2字节)
    // [30-35]: mac (6字节) - MAC地址/序列号

    // 4.1 解析电池状态
    const batteryInfo = this.parseBatteryStatus(data[4] || 0, data[5] || 0);
    console.log("电池信息:", batteryInfo);

    // 4.2 解析设备版本号 (16字节)
    const versionStr = this.parseVersionString(data.slice(8, 24));
    console.log("设备版本号:", versionStr);

    // 4.3 解析驱动编号 (2字节)
    const drvSN = shiftTo16Bit([data[24] || 0, data[25] || 0]);
    console.log("驱动编号:", drvSN);

    // 4.4 解析设备PID和VID (各2字节)
    const devicePID = shiftTo16Bit([data[26] || 0, data[27] || 0]);
    const deviceVID = shiftTo16Bit([data[28] || 0, data[29] || 0]);
    console.log("设备PID:", devicePID, "VID:", deviceVID);

    // 4.5 解析MAC地址 (6字节)
    const macAddress = this.parseMacAddress(data.slice(30, 36));
    console.log("MAC地址:", this.macToString(macAddress));

    // 5. 构建并返回设备信息对象
    const deviceInfo = {
      battStatus: batteryInfo.battStatus,
      battPercent: batteryInfo.battPercent,
      curMode: data[6] || 0,        // 当前工作模式
      reserved: data[7] || 0,       // 保留字节
      versionStr: versionStr,       // 设备版本号
      drvSN: drvSN,                 // 驱动编号
      devicePID: devicePID,         // 设备PID
      deviceVID: deviceVID,         // 设备VID
      mac: macAddress,              // MAC地址
      macStr: this.macToString(macAddress),  // MAC地址字符串
      usbMode: data[1] === 0xAA ? "USB" : "2.4G"  // 连接模式
    };

    return deviceInfo;
  }

  /**
   * 验证设备信息响应数据是否有效
   * @param {number[]} data - 设备返回的数据
   * @returns {number} 数据是否有效
   */
  isValidDeviceInfoResponse(data) {
    // 检查数据长度
    if (!data || data.length < 40) {
      console.warn("设备返回数据长度不足");
      return 0;
    }

    // 检查错误代码
    if (data[0] === 0x55 && data[2] === 0x80 && data[3] === 0xFF && data[4] === 0xFF && data[5] === 0xFF) {

      // 解析完整的状态位信息
      const status = data[6];
      if(status == 0){
        return 0;
      }else{
        return 2;
      }
    }

    // 检查命令响应标识
    if (data[2] !== 0x80 || data[3] !== 0x20) {
      return 0;
    }

    return 1;
  }

  /**
   * 解析电池状态信息
   * @param {number} battStatus - 电池状态字节
   * @param {number} battPercent - 电池百分比
   * @returns {Object} 解析后的电池信息对象
   */
  parseBatteryStatus(battStatus, battPercent) {
    // Bit7=1: 表示有5V加上
    const isPluggedIn = (battStatus & 0x80) !== 0;
    
    // Bit7=1且Bit6=1: 表示电量充满
    const isFullyCharged = isPluggedIn && ((battStatus & 0x40) !== 0);
    
    // Bit7=1且Bit6=0: 表示正在充电中
    const isCharging = isPluggedIn && ((battStatus & 0x40) === 0);
    
    // Bit7=0且Bit5=1: 表示设备处于低电状态
    const isLowBattery = !isPluggedIn && ((battStatus & 0x20) !== 0);

    return {
      battStatus: isCharging ? 1 : 0,
      battPercent: isFullyCharged ? 100 : battPercent,
      isPluggedIn,
      isFullyCharged,
      isCharging,
      isLowBattery
    };
  }

  /**
   * 解析设备版本号
   * @param {number[]} versionBytes - 版本号字节数组 (16字节)
   * @returns {string} 解析后的版本号
   */
  parseVersionString(versionBytes) {
    try {
      // 找出第一个null终止符的位置
      const nullTerminatorIndex = versionBytes.findIndex(byte => byte === 0);
      const validVersionBytes = versionBytes.slice(0, nullTerminatorIndex === -1 ? versionBytes.length : nullTerminatorIndex);
      
      // 直接使用ASCII字符解析
      let versionStr = "";
      for (let i = 0; i < validVersionBytes.length; i++) {
        const charCode = validVersionBytes[i];
        if (charCode >= 32 && charCode <= 126) {  // 可打印ASCII字符
          versionStr += String.fromCharCode(charCode);
        }
      }
      
      return versionStr.trim() || "000000V000E";  // 如果为空则返回默认值
    } catch (e) {
      console.warn("设备版本号解析失败:", e);
      return "000000V000E";  // 解析失败时返回默认值
    }
  }

  /**
   * 解析MAC地址
   * @param {number[]} macBytes - MAC地址字节数组
   * @returns {Uint8Array} MAC地址
   */
  parseMacAddress(macBytes) {
    const macAddress = new Uint8Array(6);
    for (let i = 0; i < 6; i++) {
      macAddress[i] = macBytes[i] || 0;
    }
    return macAddress;
  }

  // 获取键盘配置信息，带重试机制
  async getKeyboardConfigWithRetry(reportId = 0, maxRetries = 10, retryDelay = 50) {
    let retryCount = 0;
    let lastError = null;

    while (retryCount < maxRetries) {
      try {
        // 尝试获取键盘配置
        const keyboardConfig = await this.getKeyboardConfig(reportId);
        console.log(`键盘配置获取成功，尝试次数: ${retryCount + 1}`);
        return keyboardConfig;
      } catch (error) {
        lastError = error;
        retryCount++;
        
        if (retryCount < maxRetries) {
          console.warn(`键盘配置获取失败，正在进行第 ${retryCount + 1} 次重试...`);
          // 等待指定的延迟时间后再重试
          await this.delay(retryDelay);
        }
      }
    }

    // 如果所有重试都失败，抛出最后捕获的错误
    console.error(`键盘配置获取失败，已重试 ${maxRetries} 次`);
    throw lastError || new Error("获取键盘配置失败");
  }

  /**
   * 获取接收器信息
   * 命令格式：W 00 55 BB 00
   * 响应格式：R 00 55 BB 00 40 D0 00 0C 4B B6 05
   * @returns {Promise<Object>} 接收器信息对象
   */
  async getDongleInfo() {
    const buffer = new Array(65).fill(0);
    buffer[0] = 0x00;
    buffer[1] = 0x55;
    buffer[2] = 0xBB;
    buffer[3] = 0x00;
    
    const data = await this.setData(buffer);
    
    console.log("接收器响应数据:", data);
    // 验证响应数据
    if (data.length < 10 || data[1] !== 0xBB) {
      console.error("接收器响应数据无效:", data);
      throw new Error("获取接收器信息失败: 设备返回无效数据");
    }

    // 检查错误响应 (55 BB 80 ff ff ff ...)
    if (data[3] === 0x80 && data[4] === 0xFF && data[5] === 0xFF) {
      console.warn("接收器返回错误：设备未连接或命令重复发送");
      throw new Error("设备未连接或命令执行失败");
    }

    // 解析完整的状态位信息
    const status = data[4];
    const keyboardOnline = (status & 0x80) !== 0;        // BIT7: 键盘在线
    const mouseOnline = (status & 0x40) !== 0;           // BIT6: 鼠标在线
    const keyboardBusy = (status & 0x20) !== 0;          // BIT5: 键盘命令执行中
    const mouseBusy = (status & 0x10) !== 0;             // BIT4: 鼠标命令执行中

    // 解析MAC地址 (6字节)
    const macAddress = new Uint8Array(6);
    for (let i = 0; i < 6; i++) {
      macAddress[i] = data[5 + i] || 0;
    }

    // 检查MAC地址是否有效（全0表示未查询过设备信息）
    const macIsValid = !macAddress.every(byte => byte === 0);

    console.log("接收器详细信息:", {
      keyboardOnline,
      mouseOnline,
      keyboardBusy,      // 新增：键盘命令执行状态
      mouseBusy,         // 新增：鼠标命令执行状态
      macIsValid,        // 新增：MAC地址有效性
      macAddress: this.macToString(macAddress)
    });

    return {
      keyboardOnline,
      mouseOnline,
      keyboardBusy,      // 新增：键盘命令执行状态
      mouseBusy,         // 新增：鼠标命令执行状态
      macAddress,
      macStr: this.macToString(macAddress),
      macIsValid         // 新增：MAC地址有效性
    };
  }

  // 获取键盘配置信息
  async getKeyboardConfig(reportId = 0) {
    const buffer = new Array(65).fill(0);
    buffer[0] = reportId;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = 0x81;
    const data = await this.setData(buffer);
    
    // 检查响应是否为错误代码
    if ((data[0] === 0x55 && data[2] === 0x81 && 
         data[4] === 0xFF && data[5] === 0xFF)) {
      console.error("获取键盘配置失败");
      throw new Error("获取键盘配置失败");
    }

    if ((data[2] !== 0x81) || (data[3] !== 0x11)) {
      console.error("获取键盘配置失败", data);
      throw new Error("获取键盘配置失败");
    }
    
    // 解析第1个字节的位域
    const byte1 = data[4];
    const sleepEn = (byte1 & 0x01) !== 0;          // bit 0
    const sleepTmr = (byte1 >> 1) & 0x07;          // bits 1-3
    const lightingOnOff = ((byte1 >> 4) & 0x01) !== 0; // bit 4
    const idleTime = (byte1 >> 5) & 0x03;          // bits 5-6
    const wkSet = ((byte1 >> 7) & 0x01);     // bit 7
    
    // 解析第2个字节的位域
    const byte2 = data[5];
    const osType = byte2 & 0x03;                   // bits 0-1
    const fnFree = ((byte2 >> 2) & 0x01);          // bit 2
    
    // 复制spdBrt数组 (7字节)
    const spdBrt = [];
    for (let i = 0; i < 9; i++) {
      spdBrt.push(data[9 + i] || 0);
    }
    
    // 创建键盘配置对象
    const keyboardConfig = {
      sleepEn,
      sleepTmr,
      lightingOnOff,
      idleTime,
      wkSet,
      osType,
      fnFree,
      usbLightingSet: data[6] || 0,
      prfLightingSet: data[7] || 0,
      bleLightingSet: data[8] || 0,
      spdBrt,
      breatheMonoWaveOption: data[18] || 0,
      logoColorType: data[19] || 0,
      wkmode: data[20] || 0
    };
    
    return keyboardConfig;
  }
  
  // 设置键盘配置信息
  async setKeyboardConfig(reportId = 0, config) {
    const buffer = new Array(65).fill(0);
    buffer[0] = reportId;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = 0x82;
    buffer[4] = 0x11;

    console.log("setKeyboardConfig", config);
    
    // 构建第一个字节 (位域组合)
    let byte1 = 0;
    byte1 |= config.sleepEn ? 0x01 : 0x00;              // bit 0
    byte1 |= (config.sleepTmr & 0x07) << 1;             // bits 1-3
    byte1 |= config.lightingOnOff ? 0x10 : 0x00;        // bit 4
    byte1 |= (config.idleTime & 0x03) << 5;             // bits 5-6
    byte1 |= config.wkSet == 1 ? 0x80 : 0x00;           // bit 7
    buffer[5] = byte1;
    
    // 构建第二个字节
    let byte2 = 0;
    byte2 |= config.osType & 0x03;                      // bits 0-1
    byte2 |= config.fnFree ? 0x04 : 0x00;               // bit 2
    buffer[6] = byte2;
    
    // 设置灯光参数
    buffer[7] = config.usbLightingSet || 0;
    buffer[8] = config.prfLightingSet || 0;
    buffer[9] = config.bleLightingSet || 0;
    
    // 设置速度和亮度
    if (Array.isArray(config.spdBrt)) {
      for (let i = 0; i < 9 && i < config.spdBrt.length; i++) {
        buffer[10 + i] = config.spdBrt[i] || 0;
      }
    }
    buffer[19] = config.breatheMonoWaveOption || 0;
    // 设置LOGO灯和工作模式
    buffer[20] = config.logoColorType || 0;
    buffer[21] = config.wkmode || 0;
    
    console.log("设置键盘配置信息: ", buffer);
    return this.setData(buffer);
  }

  // 获取键盘信息
  async getKeyboardInfo() {
    const buffer = new Array(65).fill(0);
    buffer[0] = 0x00;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = 0x81;
    const data = await this.setData(buffer);
  }

  // 获取键盘按键配置信息
  async getKeyboardKeys(cmd, row_index) {
    const buffer = new Array(65).fill(0);
    buffer[0] = 0x00;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = cmd;
    buffer[4] = row_index;
    const data = await this.setData(buffer);
    return data.slice(4);
  }

  // 设置键盘按键信息
  async setKeyboardKeys(reportId = 0, cmd, row_index, dataLength, key_data) {
    const buffer = new Array(65).fill(0);
    buffer[0] = reportId;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = cmd;
    buffer[4] = dataLength;
    buffer[5] = row_index;
    for (let i = 0; i < key_data.length; i++) {
      buffer[6 + i] = key_data[i] || 0;
    }
    await this.setData(buffer);
  }

  // 获取键盘自定义按键配置信息
  async getKeyboardCustomKeys(cmd, row_index) {
    const buffer = new Array(65).fill(0);
    buffer[0] = 0x00;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = cmd;
    buffer[4] = row_index;
    const data = await this.setData(buffer);
    return data.slice(4);
  }

  // 设置键盘自定义按键信息
  async setKeyboardCustomKeys(reportId = 0, cmd, row_index, key_data) {
    const buffer = new Array(65).fill(0xff);
    buffer[0] = reportId;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = cmd;
    buffer[4] = 0x25;
    buffer[5] = row_index;
    for (let i = 0; i < key_data.length; i++) {
      buffer[6 + i] = key_data[i] || 0;
    }
    await this.setData(buffer);
  }

  // 设置键盘按键宏数据
  async setKeyboardMacro(macr_data) {
    await this.delay(1000);
    const buffer = new Array(65).fill(0);
    buffer[0] = 0x00;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = 0x86;
    buffer[4] = 0x29;
    for (let i = 0; i < macr_data.length; i++) {
      buffer[5 + i] = macr_data[i] || 0;
    }
    await this.setData(buffer);
  }

  // 恢复键盘出厂设置
  async restoreKeyboardFactorySettings(reportId = 0) {
    const buffer = new Array(65).fill(0);
    buffer[0] = reportId;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = 0x84;
    buffer[4] = 0x00;
    await this.setData(buffer);
    await this.delay(200);
    buffer[0] = reportId;
    buffer[1] = 0x55;
    buffer[2] = 0xAA;
    buffer[3] = 0x83;
    buffer[4] = 0x00;
    await this.setData(buffer);
  }

  // 根据设备信息解析电池状态
  getBatteryStatus(deviceInfo) {
    if (!deviceInfo) return { status: "unknown", percent: 0 };
    
    const battStatus = deviceInfo.battStatus;
    const battPercent = deviceInfo.battPercent;
    
    // Bit7=1: 表示有5V加上
    const isPluggedIn = (battStatus & 0x80) !== 0;
    
    // Bit7=1且Bit6=1: 表示电量充满
    const isFullyCharged = isPluggedIn && ((battStatus & 0x40) !== 0);
    
    // Bit7=1且Bit6=0: 表示正在充电中
    const isCharging = isPluggedIn && ((battStatus & 0x40) === 0);
    
    // Bit7=0且Bit5=1: 表示设备处于低电状态
    const isLowBattery = !isPluggedIn && ((battStatus & 0x20) !== 0);
    
    let status = "normal";
    
    if (isFullyCharged) {
      status = "full";      // 电量已充满
    } else if (isCharging) {
      status = "charging";  // 正在充电中
    } else if (isLowBattery) {
      status = "low";       // 低电量状态
    }
    
    return {
      status,
      percent: battPercent,
      isPluggedIn,
      isFullyCharged,
      isCharging,
      isLowBattery,
      raw: battStatus
    };
  }

  // 将MAC地址转换为字符串格式
  macToString(mac) {
    if (!mac || mac.length !== 6) return "00:00:00:00:00:00";
    
    return Array.from(mac)
      .map(byte => byte.toString(16).padStart(2, '0'))
      .join(':')
      .toUpperCase();
  }

  // 在组件销毁时清理
  cleanup() {
    if (this.writeTimeout) {
      clearTimeout(this.writeTimeout);
    }
  }
}